<template>
  <div class="webpack-loader-test-event">
    <h1>{{ message }}</h1>
    <p>loader是将指定格式的资源⽂件转化成⼀定格式输出,例如sass-loader将scss⽂件转化成css⽂件, babel-loader将ES6转化成ES5.最终都会转化为webpack能识别的js模块。</p>
    <h2>loader的执行顺序</h2>
    <p>loader的执行顺序是从后向前，当我们配置如下，他的执顺序是：./webpack-loader/test-css-loader.js =》css-loader =》style-loader =》vue-style-loader </p>
    <code>
      <pre>
        rules: [
              {
                  test: /\.css$/,
                  use: [
                      'vue-style-loader',
                      'style-loader',
                      'css-loader',
                      './webpack-loader/test-css-loader.js',
                  ]
              }
          ]
      </pre>
    </code>
    <p>loader是一个导出为函数的 JavaScript模块，一个最简单的loader,读完直接返回</p>
    <code>
      <pre>
        module.exports = function(content, map, meta) { return content;}
        // content： string | Buffer 默认情况下是UTF-8 字符串
        // 如果想用二进制原数据 需要设置 raw
        module.exports.raw = true;
      </pre>
    </code>
    <p><span class="red">*</span> 方法和node一样遵循error first的原则。</p>
    <p>在loader中提供了this，提供了一些loader API，参考<a href="https://webpack.js.org/api/loaders/">https://webpack.js.org/api/loaders/</a>，下面简单介绍几个常用的：</p>
    <ul>
      <li>callback： this.callback(err, content, sourceMap, AST)，如果要返回除了content以外的东西如AST, sourceMap，需要用callback，同时需要return undefined;告诉webpack 结果在callback中。</li>
      <li>context：this.context返回处理文件的目录</li>
      <li>loadMoudle：当需要依赖其他loader时可以</li>
      <li>async： 当处理结果需要异步时，可以用this.async；
        <code>
          <pre>
            module.exports = function(content, map, meta) {
                let callback = this.async();
                fs.readFile('test.js', 'utf-8', function(err, source) {
                    if(err) return callback(err);
                    callback(null, source);
                });
            }
          </pre>
        </code>
      </li>
      <li>resolve：this.resolve(path)解析制定路径的文件，</li>
      <li>emitFile：输出一个文件，this.emitFile(name: string, content: Buffer|string, sourceMap: {...})</li>
      <li>loadModule：需要依赖其他文件处理的结果的话，可以通过
        <code><pre>this.loadModule(request: string, callback: function(err, source, sourceMap, module))</pre></code>
      </li>
      <li>loaderIndex：this.loaderIndex可以获取到当前时第几个loader，从起那往后排</li>
      <li>mode：this.mode获取当前的处理环境</li>
      <li>getOptions： 可以在webpack5中用this.getOptions替代loader-utils 获取loader参数</li>
      <li>emitWarning：抛出警告
        <code><pre>this.emitWarning(warning: Error)</pre></code>
      </li>
      <li>emitError：抛出异常，会直接阻断webpack的进行
        <code><pre>this.emitError(error: Error)</pre></code>
      </li>
    </ul>
    <h2>简单写了两个例子</h2>
    <h3>自定义css</h3>
    <code>
      <pre>
        //loader

        function fontParse(fontStr) {
            let args = fontStr.replace(/^(.*\()|(\).*)$/g, '');
            let argsList = args.split(',');

            cssName = ['font-size', 'color'].map((item, index) => {
                return `${item}:${argsList[index]}`;
            });
            let cssStr = cssName.join(';');

            return cssStr;
        }
        module.exports = function (content, sourceMaps, meta) {
            content = content.replace(/font\((.|\n)*?\)/ig, (str) => fontParse(str));
            
            return content;
        }

        // webpack 配置
        {
            test: /\.css$/,
            use: [
                'vue-style-loader',
                ENV=== 'production'? MiniCssExtractPlugin.loader:'style-loader',
                'css-loader',
                {
                    loader: './webpack-loader/test-css-loader.js',
                    options: {
                        path: 'abc'
                    }
                }
            ]
        },

        // 需要转的文件

        &lt;style lang="css"&gt;
          .left-list{
            font(20px, green);
          }
        &lt;/style&gt;

        // 最终结果

        .left-list{
            font-size:20px;color: green;
        }
      </pre>
    </code>
    <h3>转换自定义 .tt 文件</h3>
    <code>
      <pre>
        // loader
        module.exports = function(source){
            return source.replace(/c/g, 'console.error').replace(/a/g, 'alert');
        }

        // test.tt 
        c('我是自己转的');
        a('我是自己转的');

        // 转换结果
        console.error("我是自己转的"),alert("我是自己转的")
      </pre>
    </code>
    <p>最后return的一定要是webpack能识别的js，返回其他的话需要再依赖其他loader继续转化。</p>
    <p class="page-end">The End</p>
  </div>
</template>

<script lang="ts">
    import Vue from 'vue';
    import Component from 'vue-class-component';
    @Component({
        // 所有的组件选项都可以放在这里
    })
    export default class loader extends Vue {
        // data 在这
        message: string = '自定义loader'

        created () {
        }
    }
</script>
<style lang="scss">
.webpack-loader-test-event{
}
</style>